// LoongArchAsmParser.cpp - Parse LoongArch assembly to MCInst instructions -=//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "MCTargetDesc/LoongArchInstPrinter.h"
#include "MCTargetDesc/LoongArchMCExpr.h"
#include "MCTargetDesc/LoongArchMCTargetDesc.h"
#include "MCTargetDesc/LoongArchMatInt.h"
#include "TargetInfo/LoongArchTargetInfo.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCInstBuilder.h"
#include "llvm/MC/MCInstrInfo.h"
#include "llvm/MC/MCParser/MCAsmLexer.h"
#include "llvm/MC/MCParser/MCParsedAsmOperand.h"
#include "llvm/MC/MCParser/MCTargetAsmParser.h"
#include "llvm/MC/MCRegisterInfo.h"
#include "llvm/MC/MCStreamer.h"
#include "llvm/MC/MCSubtargetInfo.h"
#include "llvm/MC/MCValue.h"
#include "llvm/MC/TargetRegistry.h"
#include "llvm/Support/Casting.h"

using namespace llvm;

#define DEBUG_TYPE "loongarch-asm-parser"

namespace {
class LoongArchAsmParser : public MCTargetAsmParser {
  SMLoc getLoc() const { return getParser().getTok().getLoc(); }
  bool is64Bit() const { return getSTI().hasFeature(LoongArch::Feature64Bit); }

  struct Inst {
    unsigned Opc;
    LoongArchMCExpr::VariantKind VK;
    Inst(unsigned Opc,
         LoongArchMCExpr::VariantKind VK = LoongArchMCExpr::VK_LoongArch_None)
        : Opc(Opc), VK(VK) {}
  };
  using InstSeq = SmallVector<Inst>;

  /// Parse a register as used in CFI directives.
  bool parseRegister(MCRegister &RegNo, SMLoc &StartLoc,
                     SMLoc &EndLoc) override;
  OperandMatchResultTy tryParseRegister(MCRegister &RegNo, SMLoc &StartLoc,
                                        SMLoc &EndLoc) override;

  bool ParseInstruction(ParseInstructionInfo &Info, StringRef Name,
                        SMLoc NameLoc, OperandVector &Operands) override;

  bool ParseDirective(AsmToken DirectiveID) override { return true; }

  bool MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode,
                               OperandVector &Operands, MCStreamer &Out,
                               uint64_t &ErrorInfo,
                               bool MatchingInlineAsm) override;

  unsigned checkTargetMatchPredicate(MCInst &Inst) override;

  unsigned validateTargetOperandClass(MCParsedAsmOperand &Op,
                                      unsigned Kind) override;

  bool generateImmOutOfRangeError(OperandVector &Operands, uint64_t ErrorInfo,
                                  int64_t Lower, int64_t Upper, Twine Msg);

  /// Helper for processing MC instructions that have been successfully matched
  /// by MatchAndEmitInstruction.
  bool processInstruction(MCInst &Inst, SMLoc IDLoc, OperandVector &Operands,
                          MCStreamer &Out);

// Auto-generated instruction matching functions.
#define GET_ASSEMBLER_HEADER
#include "LoongArchGenAsmMatcher.inc"

  OperandMatchResultTy parseRegister(OperandVector &Operands);
  OperandMatchResultTy parseImmediate(OperandVector &Operands);
  OperandMatchResultTy parseOperandWithModifier(OperandVector &Operands);
  OperandMatchResultTy parseSImm26Operand(OperandVector &Operands);
  OperandMatchResultTy parseAtomicMemOp(OperandVector &Operands);

  bool parseOperand(OperandVector &Operands, StringRef Mnemonic);

  // Helper to emit the sequence of instructions generated by the
  // "emitLoadAddress*" functions.
  void emitLAInstSeq(MCRegister DestReg, MCRegister TmpReg,
                     const MCExpr *Symbol, SmallVectorImpl<Inst> &Insts,
                     SMLoc IDLoc, MCStreamer &Out);

  // Helper to emit pseudo instruction "la.abs $rd, sym".
  void emitLoadAddressAbs(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out);

  // Helper to emit pseudo instruction "la.pcrel $rd, sym".
  void emitLoadAddressPcrel(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out);
  // Helper to emit pseudo instruction "la.pcrel $rd, $rj, sym".
  void emitLoadAddressPcrelLarge(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out);

  // Helper to emit pseudo instruction "la.got $rd, sym".
  void emitLoadAddressGot(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out);
  // Helper to emit pseudo instruction "la.got $rd, $rj, sym".
  void emitLoadAddressGotLarge(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out);

  // Helper to emit pseudo instruction "la.tls.le $rd, sym".
  void emitLoadAddressTLSLE(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out);

  // Helper to emit pseudo instruction "la.tls.ie $rd, sym".
  void emitLoadAddressTLSIE(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out);
  // Helper to emit pseudo instruction "la.tls.ie $rd, $rj, sym".
  void emitLoadAddressTLSIELarge(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out);

  // Helper to emit pseudo instruction "la.tls.ld $rd, sym".
  void emitLoadAddressTLSLD(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out);
  // Helper to emit pseudo instruction "la.tls.ld $rd, $rj, sym".
  void emitLoadAddressTLSLDLarge(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out);

  // Helper to emit pseudo instruction "la.tls.gd $rd, sym".
  void emitLoadAddressTLSGD(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out);
  // Helper to emit pseudo instruction "la.tls.gd $rd, $rj, sym".
  void emitLoadAddressTLSGDLarge(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out);

  // Helper to emit pseudo instruction "li.w/d $rd, $imm".
  void emitLoadImm(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out);

public:
  enum LoongArchMatchResultTy {
    Match_Dummy = FIRST_TARGET_MATCH_RESULT_TY,
    Match_RequiresMsbNotLessThanLsb,
    Match_RequiresOpnd2NotR0R1,
    Match_RequiresAMORdDifferRkRj,
    Match_RequiresLAORdDifferRj,
#define GET_OPERAND_DIAGNOSTIC_TYPES
#include "LoongArchGenAsmMatcher.inc"
#undef GET_OPERAND_DIAGNOSTIC_TYPES
  };

  static bool classifySymbolRef(const MCExpr *Expr,
                                LoongArchMCExpr::VariantKind &Kind);

  LoongArchAsmParser(const MCSubtargetInfo &STI, MCAsmParser &Parser,
                     const MCInstrInfo &MII, const MCTargetOptions &Options)
      : MCTargetAsmParser(Options, STI, MII) {
    Parser.addAliasForDirective(".half", ".2byte");
    Parser.addAliasForDirective(".hword", ".2byte");
    Parser.addAliasForDirective(".word", ".4byte");
    Parser.addAliasForDirective(".dword", ".8byte");

    // Initialize the set of available features.
    setAvailableFeatures(ComputeAvailableFeatures(STI.getFeatureBits()));
  }
};

// Instances of this class represent a parsed LoongArch machine instruction.
class LoongArchOperand : public MCParsedAsmOperand {
  enum class KindTy {
    Token,
    Register,
    Immediate,
  } Kind;

  struct RegOp {
    MCRegister RegNum;
  };

  struct ImmOp {
    const MCExpr *Val;
  };

  SMLoc StartLoc, EndLoc;
  union {
    StringRef Tok;
    struct RegOp Reg;
    struct ImmOp Imm;
  };

public:
  LoongArchOperand(KindTy K) : MCParsedAsmOperand(), Kind(K) {}

  bool isToken() const override { return Kind == KindTy::Token; }
  bool isReg() const override { return Kind == KindTy::Register; }
  bool isImm() const override { return Kind == KindTy::Immediate; }
  bool isMem() const override { return false; }
  void setReg(MCRegister PhysReg) { Reg.RegNum = PhysReg; }
  bool isGPR() const {
    return Kind == KindTy::Register &&
           LoongArchMCRegisterClasses[LoongArch::GPRRegClassID].contains(
               Reg.RegNum);
  }

  static bool evaluateConstantImm(const MCExpr *Expr, int64_t &Imm,
                                  LoongArchMCExpr::VariantKind &VK) {
    if (auto *LE = dyn_cast<LoongArchMCExpr>(Expr)) {
      VK = LE->getKind();
      return false;
    }

    if (auto CE = dyn_cast<MCConstantExpr>(Expr)) {
      Imm = CE->getValue();
      return true;
    }

    return false;
  }

  template <unsigned N, int P = 0> bool isUImm() const {
    if (!isImm())
      return false;

    int64_t Imm;
    LoongArchMCExpr::VariantKind VK = LoongArchMCExpr::VK_LoongArch_None;
    bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK);
    return IsConstantImm && isUInt<N>(Imm - P) &&
           VK == LoongArchMCExpr::VK_LoongArch_None;
  }

  template <unsigned N, unsigned S = 0> bool isSImm() const {
    if (!isImm())
      return false;

    int64_t Imm;
    LoongArchMCExpr::VariantKind VK = LoongArchMCExpr::VK_LoongArch_None;
    bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK);
    return IsConstantImm && isShiftedInt<N, S>(Imm) &&
           VK == LoongArchMCExpr::VK_LoongArch_None;
  }

  bool isBareSymbol() const {
    int64_t Imm;
    LoongArchMCExpr::VariantKind VK = LoongArchMCExpr::VK_LoongArch_None;
    // Must be of 'immediate' type but not a constant.
    if (!isImm() || evaluateConstantImm(getImm(), Imm, VK))
      return false;
    return LoongArchAsmParser::classifySymbolRef(getImm(), VK) &&
           VK == LoongArchMCExpr::VK_LoongArch_None;
  }

  bool isUImm2() const { return isUImm<2>(); }
  bool isUImm2plus1() const { return isUImm<2, 1>(); }
  bool isUImm3() const { return isUImm<3>(); }
  bool isUImm5() const { return isUImm<5>(); }
  bool isUImm6() const { return isUImm<6>(); }
  bool isUImm8() const { return isUImm<8>(); }
  bool isSImm12() const { return isSImm<12>(); }

  bool isSImm12addlike() const {
    if (!isImm())
      return false;

    int64_t Imm;
    LoongArchMCExpr::VariantKind VK = LoongArchMCExpr::VK_LoongArch_None;
    bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK);
    bool IsValidKind = VK == LoongArchMCExpr::VK_LoongArch_None ||
                       VK == LoongArchMCExpr::VK_LoongArch_PCALA_LO12 ||
                       VK == LoongArchMCExpr::VK_LoongArch_GOT_PC_LO12 ||
                       VK == LoongArchMCExpr::VK_LoongArch_TLS_IE_PC_LO12;
    return IsConstantImm
               ? isInt<12>(Imm) && IsValidKind
               : LoongArchAsmParser::classifySymbolRef(getImm(), VK) &&
                     IsValidKind;
  }

  bool isSImm12lu52id() const {
    if (!isImm())
      return false;

    int64_t Imm;
    LoongArchMCExpr::VariantKind VK = LoongArchMCExpr::VK_LoongArch_None;
    bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK);
    bool IsValidKind = VK == LoongArchMCExpr::VK_LoongArch_None ||
                       VK == LoongArchMCExpr::VK_LoongArch_ABS64_HI12 ||
                       VK == LoongArchMCExpr::VK_LoongArch_PCALA64_HI12 ||
                       VK == LoongArchMCExpr::VK_LoongArch_GOT64_HI12 ||
                       VK == LoongArchMCExpr::VK_LoongArch_GOT64_PC_HI12 ||
                       VK == LoongArchMCExpr::VK_LoongArch_TLS_LE64_HI12 ||
                       VK == LoongArchMCExpr::VK_LoongArch_TLS_IE64_HI12 ||
                       VK == LoongArchMCExpr::VK_LoongArch_TLS_IE64_PC_HI12;
    return IsConstantImm
               ? isInt<12>(Imm) && IsValidKind
               : LoongArchAsmParser::classifySymbolRef(getImm(), VK) &&
                     IsValidKind;
  }

  bool isUImm12() const { return isUImm<12>(); }

  bool isUImm12ori() const {
    if (!isImm())
      return false;

    int64_t Imm;
    LoongArchMCExpr::VariantKind VK = LoongArchMCExpr::VK_LoongArch_None;
    bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK);
    bool IsValidKind = VK == LoongArchMCExpr::VK_LoongArch_None ||
                       VK == LoongArchMCExpr::VK_LoongArch_ABS_LO12 ||
                       VK == LoongArchMCExpr::VK_LoongArch_PCALA_LO12 ||
                       VK == LoongArchMCExpr::VK_LoongArch_GOT_LO12 ||
                       VK == LoongArchMCExpr::VK_LoongArch_GOT_PC_LO12 ||
                       VK == LoongArchMCExpr::VK_LoongArch_TLS_LE_LO12 ||
                       VK == LoongArchMCExpr::VK_LoongArch_TLS_IE_LO12 ||
                       VK == LoongArchMCExpr::VK_LoongArch_TLS_IE_PC_LO12;
    return IsConstantImm
               ? isUInt<12>(Imm) && IsValidKind
               : LoongArchAsmParser::classifySymbolRef(getImm(), VK) &&
                     IsValidKind;
  }

  bool isUImm14() const { return isUImm<14>(); }
  bool isUImm15() const { return isUImm<15>(); }

  bool isSImm14lsl2() const { return isSImm<14, 2>(); }
  bool isSImm16() const { return isSImm<16>(); }

  bool isSImm16lsl2() const {
    if (!isImm())
      return false;

    int64_t Imm;
    LoongArchMCExpr::VariantKind VK = LoongArchMCExpr::VK_LoongArch_None;
    bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK);
    bool IsValidKind = VK == LoongArchMCExpr::VK_LoongArch_None ||
                       VK == LoongArchMCExpr::VK_LoongArch_B16 ||
                       VK == LoongArchMCExpr::VK_LoongArch_PCALA_LO12;
    return IsConstantImm
               ? isShiftedInt<16, 2>(Imm) && IsValidKind
               : LoongArchAsmParser::classifySymbolRef(getImm(), VK) &&
                     IsValidKind;
  }

  bool isSImm20() const { return isSImm<20>(); }

  bool isSImm20pcalau12i() const {
    if (!isImm())
      return false;

    int64_t Imm;
    LoongArchMCExpr::VariantKind VK = LoongArchMCExpr::VK_LoongArch_None;
    bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK);
    bool IsValidKind = VK == LoongArchMCExpr::VK_LoongArch_None ||
                       VK == LoongArchMCExpr::VK_LoongArch_PCALA_HI20 ||
                       VK == LoongArchMCExpr::VK_LoongArch_GOT_PC_HI20 ||
                       VK == LoongArchMCExpr::VK_LoongArch_TLS_IE_PC_HI20 ||
                       VK == LoongArchMCExpr::VK_LoongArch_TLS_LD_PC_HI20 ||
                       VK == LoongArchMCExpr::VK_LoongArch_TLS_GD_PC_HI20;
    return IsConstantImm
               ? isInt<20>(Imm) && IsValidKind
               : LoongArchAsmParser::classifySymbolRef(getImm(), VK) &&
                     IsValidKind;
  }

  bool isSImm20lu12iw() const {
    if (!isImm())
      return false;

    int64_t Imm;
    LoongArchMCExpr::VariantKind VK = LoongArchMCExpr::VK_LoongArch_None;
    bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK);
    bool IsValidKind = VK == LoongArchMCExpr::VK_LoongArch_None ||
                       VK == LoongArchMCExpr::VK_LoongArch_ABS_HI20 ||
                       VK == LoongArchMCExpr::VK_LoongArch_GOT_HI20 ||
                       VK == LoongArchMCExpr::VK_LoongArch_TLS_GD_HI20 ||
                       VK == LoongArchMCExpr::VK_LoongArch_TLS_LD_HI20 ||
                       VK == LoongArchMCExpr::VK_LoongArch_TLS_IE_HI20 ||
                       VK == LoongArchMCExpr::VK_LoongArch_TLS_LE_HI20;
    return IsConstantImm
               ? isInt<20>(Imm) && IsValidKind
               : LoongArchAsmParser::classifySymbolRef(getImm(), VK) &&
                     IsValidKind;
  }

  bool isSImm20lu32id() const {
    if (!isImm())
      return false;

    int64_t Imm;
    LoongArchMCExpr::VariantKind VK = LoongArchMCExpr::VK_LoongArch_None;
    bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK);
    bool IsValidKind = VK == LoongArchMCExpr::VK_LoongArch_None ||
                       VK == LoongArchMCExpr::VK_LoongArch_ABS64_LO20 ||
                       VK == LoongArchMCExpr::VK_LoongArch_PCALA64_LO20 ||
                       VK == LoongArchMCExpr::VK_LoongArch_GOT64_LO20 ||
                       VK == LoongArchMCExpr::VK_LoongArch_GOT64_PC_LO20 ||
                       VK == LoongArchMCExpr::VK_LoongArch_TLS_IE64_LO20 ||
                       VK == LoongArchMCExpr::VK_LoongArch_TLS_IE64_PC_LO20 ||
                       VK == LoongArchMCExpr::VK_LoongArch_TLS_LE64_LO20;

    return IsConstantImm
               ? isInt<20>(Imm) && IsValidKind
               : LoongArchAsmParser::classifySymbolRef(getImm(), VK) &&
                     IsValidKind;
  }

  bool isSImm21lsl2() const {
    if (!isImm())
      return false;

    int64_t Imm;
    LoongArchMCExpr::VariantKind VK = LoongArchMCExpr::VK_LoongArch_None;
    bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK);
    bool IsValidKind = VK == LoongArchMCExpr::VK_LoongArch_None ||
                       VK == LoongArchMCExpr::VK_LoongArch_B21;
    return IsConstantImm
               ? isShiftedInt<21, 2>(Imm) && IsValidKind
               : LoongArchAsmParser::classifySymbolRef(getImm(), VK) &&
                     IsValidKind;
  }

  bool isSImm26Operand() const {
    if (!isImm())
      return false;

    int64_t Imm;
    LoongArchMCExpr::VariantKind VK = LoongArchMCExpr::VK_LoongArch_None;
    bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK);
    bool IsValidKind = VK == LoongArchMCExpr::VK_LoongArch_None ||
                       VK == LoongArchMCExpr::VK_LoongArch_CALL ||
                       VK == LoongArchMCExpr::VK_LoongArch_CALL_PLT ||
                       VK == LoongArchMCExpr::VK_LoongArch_B26;
    return IsConstantImm
               ? isShiftedInt<26, 2>(Imm) && IsValidKind
               : LoongArchAsmParser::classifySymbolRef(getImm(), VK) &&
                     IsValidKind;
  }

  bool isImm32() const { return isSImm<32>() || isUImm<32>(); }

  /// Gets location of the first token of this operand.
  SMLoc getStartLoc() const override { return StartLoc; }
  /// Gets location of the last token of this operand.
  SMLoc getEndLoc() const override { return EndLoc; }

  unsigned getReg() const override {
    assert(Kind == KindTy::Register && "Invalid type access!");
    return Reg.RegNum.id();
  }

  const MCExpr *getImm() const {
    assert(Kind == KindTy::Immediate && "Invalid type access!");
    return Imm.Val;
  }

  StringRef getToken() const {
    assert(Kind == KindTy::Token && "Invalid type access!");
    return Tok;
  }

  void print(raw_ostream &OS) const override {
    auto RegName = [](MCRegister Reg) {
      if (Reg)
        return LoongArchInstPrinter::getRegisterName(Reg);
      else
        return "noreg";
    };

    switch (Kind) {
    case KindTy::Immediate:
      OS << *getImm();
      break;
    case KindTy::Register:
      OS << "<register " << RegName(getReg()) << ">";
      break;
    case KindTy::Token:
      OS << "'" << getToken() << "'";
      break;
    }
  }

  static std::unique_ptr<LoongArchOperand> createToken(StringRef Str, SMLoc S) {
    auto Op = std::make_unique<LoongArchOperand>(KindTy::Token);
    Op->Tok = Str;
    Op->StartLoc = S;
    Op->EndLoc = S;
    return Op;
  }

  static std::unique_ptr<LoongArchOperand> createReg(unsigned RegNo, SMLoc S,
                                                     SMLoc E) {
    auto Op = std::make_unique<LoongArchOperand>(KindTy::Register);
    Op->Reg.RegNum = RegNo;
    Op->StartLoc = S;
    Op->EndLoc = E;
    return Op;
  }

  static std::unique_ptr<LoongArchOperand> createImm(const MCExpr *Val, SMLoc S,
                                                     SMLoc E) {
    auto Op = std::make_unique<LoongArchOperand>(KindTy::Immediate);
    Op->Imm.Val = Val;
    Op->StartLoc = S;
    Op->EndLoc = E;
    return Op;
  }

  void addExpr(MCInst &Inst, const MCExpr *Expr) const {
    if (auto CE = dyn_cast<MCConstantExpr>(Expr))
      Inst.addOperand(MCOperand::createImm(CE->getValue()));
    else
      Inst.addOperand(MCOperand::createExpr(Expr));
  }

  // Used by the TableGen Code.
  void addRegOperands(MCInst &Inst, unsigned N) const {
    assert(N == 1 && "Invalid number of operands!");
    Inst.addOperand(MCOperand::createReg(getReg()));
  }
  void addImmOperands(MCInst &Inst, unsigned N) const {
    assert(N == 1 && "Invalid number of operands!");
    addExpr(Inst, getImm());
  }
};
} // end namespace

#define GET_REGISTER_MATCHER
#define GET_SUBTARGET_FEATURE_NAME
#define GET_MATCHER_IMPLEMENTATION
#define GET_MNEMONIC_SPELL_CHECKER
#include "LoongArchGenAsmMatcher.inc"

static MCRegister convertFPR32ToFPR64(MCRegister Reg) {
  assert(Reg >= LoongArch::F0 && Reg <= LoongArch::F31 && "Invalid register");
  return Reg - LoongArch::F0 + LoongArch::F0_64;
}

// Attempts to match Name as a register (either using the default name or
// alternative ABI names), setting RegNo to the matching register. Upon
// failure, returns true and sets RegNo to 0.
static bool matchRegisterNameHelper(MCRegister &RegNo, StringRef Name) {
  RegNo = MatchRegisterName(Name);
  // The 32-bit and 64-bit FPRs have the same asm name. Check that the initial
  // match always matches the 32-bit variant, and not the 64-bit one.
  assert(!(RegNo >= LoongArch::F0_64 && RegNo <= LoongArch::F31_64));
  // The default FPR register class is based on the tablegen enum ordering.
  static_assert(LoongArch::F0 < LoongArch::F0_64,
                "FPR matching must be updated");
  if (RegNo == LoongArch::NoRegister)
    RegNo = MatchRegisterAltName(Name);

  return RegNo == LoongArch::NoRegister;
}

bool LoongArchAsmParser::parseRegister(MCRegister &RegNo, SMLoc &StartLoc,
                                       SMLoc &EndLoc) {
  return Error(getLoc(), "invalid register number");
}

OperandMatchResultTy LoongArchAsmParser::tryParseRegister(MCRegister &RegNo,
                                                          SMLoc &StartLoc,
                                                          SMLoc &EndLoc) {
  llvm_unreachable("Unimplemented function.");
}

bool LoongArchAsmParser::classifySymbolRef(const MCExpr *Expr,
                                           LoongArchMCExpr::VariantKind &Kind) {
  Kind = LoongArchMCExpr::VK_LoongArch_None;

  if (const LoongArchMCExpr *RE = dyn_cast<LoongArchMCExpr>(Expr)) {
    Kind = RE->getKind();
    Expr = RE->getSubExpr();
  }

  MCValue Res;
  if (Expr->evaluateAsRelocatable(Res, nullptr, nullptr))
    return Res.getRefKind() == LoongArchMCExpr::VK_LoongArch_None;
  return false;
}

OperandMatchResultTy
LoongArchAsmParser::parseRegister(OperandVector &Operands) {
  if (getLexer().getTok().isNot(AsmToken::Dollar))
    return MatchOperand_NoMatch;

  // Eat the $ prefix.
  getLexer().Lex();
  if (getLexer().getKind() != AsmToken::Identifier)
    return MatchOperand_NoMatch;

  StringRef Name = getLexer().getTok().getIdentifier();
  MCRegister RegNo;
  matchRegisterNameHelper(RegNo, Name);
  if (RegNo == LoongArch::NoRegister)
    return MatchOperand_NoMatch;

  SMLoc S = getLoc();
  SMLoc E = SMLoc::getFromPointer(S.getPointer() + Name.size());
  getLexer().Lex();
  Operands.push_back(LoongArchOperand::createReg(RegNo, S, E));

  return MatchOperand_Success;
}

OperandMatchResultTy
LoongArchAsmParser::parseImmediate(OperandVector &Operands) {
  SMLoc S = getLoc();
  SMLoc E;
  const MCExpr *Res;

  switch (getLexer().getKind()) {
  default:
    return MatchOperand_NoMatch;
  case AsmToken::LParen:
  case AsmToken::Dot:
  case AsmToken::Minus:
  case AsmToken::Plus:
  case AsmToken::Exclaim:
  case AsmToken::Tilde:
  case AsmToken::Integer:
  case AsmToken::String:
  case AsmToken::Identifier:
    if (getParser().parseExpression(Res, E))
      return MatchOperand_ParseFail;
    break;
  case AsmToken::Percent:
    return parseOperandWithModifier(Operands);
  }

  Operands.push_back(LoongArchOperand::createImm(Res, S, E));
  return MatchOperand_Success;
}

OperandMatchResultTy
LoongArchAsmParser::parseOperandWithModifier(OperandVector &Operands) {
  SMLoc S = getLoc();
  SMLoc E;

  if (getLexer().getKind() != AsmToken::Percent) {
    Error(getLoc(), "expected '%' for operand modifier");
    return MatchOperand_ParseFail;
  }

  getParser().Lex(); // Eat '%'

  if (getLexer().getKind() != AsmToken::Identifier) {
    Error(getLoc(), "expected valid identifier for operand modifier");
    return MatchOperand_ParseFail;
  }
  StringRef Identifier = getParser().getTok().getIdentifier();
  LoongArchMCExpr::VariantKind VK =
      LoongArchMCExpr::getVariantKindForName(Identifier);
  if (VK == LoongArchMCExpr::VK_LoongArch_Invalid) {
    Error(getLoc(), "unrecognized operand modifier");
    return MatchOperand_ParseFail;
  }

  getParser().Lex(); // Eat the identifier
  if (getLexer().getKind() != AsmToken::LParen) {
    Error(getLoc(), "expected '('");
    return MatchOperand_ParseFail;
  }
  getParser().Lex(); // Eat '('

  const MCExpr *SubExpr;
  if (getParser().parseParenExpression(SubExpr, E)) {
    return MatchOperand_ParseFail;
  }

  const MCExpr *ModExpr = LoongArchMCExpr::create(SubExpr, VK, getContext());
  Operands.push_back(LoongArchOperand::createImm(ModExpr, S, E));
  return MatchOperand_Success;
}

OperandMatchResultTy
LoongArchAsmParser::parseSImm26Operand(OperandVector &Operands) {
  SMLoc S = getLoc();
  const MCExpr *Res;

  if (getLexer().getKind() == AsmToken::Percent)
    return parseOperandWithModifier(Operands);

  if (getLexer().getKind() != AsmToken::Identifier)
    return MatchOperand_NoMatch;

  StringRef Identifier;
  if (getParser().parseIdentifier(Identifier))
    return MatchOperand_ParseFail;

  SMLoc E = SMLoc::getFromPointer(S.getPointer() + Identifier.size());

  MCSymbol *Sym = getContext().getOrCreateSymbol(Identifier);
  Res = MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_None, getContext());
  Res = LoongArchMCExpr::create(Res, LoongArchMCExpr::VK_LoongArch_CALL,
                                getContext());
  Operands.push_back(LoongArchOperand::createImm(Res, S, E));
  return MatchOperand_Success;
}

OperandMatchResultTy
LoongArchAsmParser::parseAtomicMemOp(OperandVector &Operands) {
  // Parse "$r*".
  if (parseRegister(Operands) != MatchOperand_Success)
    return MatchOperand_NoMatch;

  // If there is a next operand and it is 0, ignore it. Otherwise print a
  // diagnostic message.
  if (getLexer().is(AsmToken::Comma)) {
    getLexer().Lex(); // Consume comma token.
    int64_t ImmVal;
    SMLoc ImmStart = getLoc();
    if (getParser().parseIntToken(ImmVal, "expected optional integer offset"))
      return MatchOperand_ParseFail;
    if (ImmVal) {
      Error(ImmStart, "optional integer offset must be 0");
      return MatchOperand_ParseFail;
    }
  }

  return MatchOperand_Success;
}
/// Looks at a token type and creates the relevant operand from this
/// information, adding to Operands. Return true upon an error.
bool LoongArchAsmParser::parseOperand(OperandVector &Operands,
                                      StringRef Mnemonic) {
  // Check if the current operand has a custom associated parser, if so, try to
  // custom parse the operand, or fallback to the general approach.
  OperandMatchResultTy Result =
      MatchOperandParserImpl(Operands, Mnemonic, /*ParseForAllFeatures=*/true);
  if (Result == MatchOperand_Success)
    return false;
  if (Result == MatchOperand_ParseFail)
    return true;

  if (parseRegister(Operands) == MatchOperand_Success ||
      parseImmediate(Operands) == MatchOperand_Success)
    return false;

  // Finally we have exhausted all options and must declare defeat.
  Error(getLoc(), "unknown operand");
  return true;
}

bool LoongArchAsmParser::ParseInstruction(ParseInstructionInfo &Info,
                                          StringRef Name, SMLoc NameLoc,
                                          OperandVector &Operands) {
  // First operand in MCInst is instruction mnemonic.
  Operands.push_back(LoongArchOperand::createToken(Name, NameLoc));

  // If there are no more operands, then finish.
  if (parseOptionalToken(AsmToken::EndOfStatement))
    return false;

  // Parse first operand.
  if (parseOperand(Operands, Name))
    return true;

  // Parse until end of statement, consuming commas between operands.
  while (parseOptionalToken(AsmToken::Comma))
    if (parseOperand(Operands, Name))
      return true;

  // Parse end of statement and return successfully.
  if (parseOptionalToken(AsmToken::EndOfStatement))
    return false;

  SMLoc Loc = getLexer().getLoc();
  getParser().eatToEndOfStatement();
  return Error(Loc, "unexpected token");
}

void LoongArchAsmParser::emitLAInstSeq(MCRegister DestReg, MCRegister TmpReg,
                                       const MCExpr *Symbol,
                                       SmallVectorImpl<Inst> &Insts,
                                       SMLoc IDLoc, MCStreamer &Out) {
  MCContext &Ctx = getContext();
  for (LoongArchAsmParser::Inst &Inst : Insts) {
    unsigned Opc = Inst.Opc;
    LoongArchMCExpr::VariantKind VK = Inst.VK;
    const LoongArchMCExpr *LE = LoongArchMCExpr::create(Symbol, VK, Ctx);
    switch (Opc) {
    default:
      llvm_unreachable("unexpected opcode");
    case LoongArch::PCALAU12I:
    case LoongArch::LU12I_W:
      Out.emitInstruction(MCInstBuilder(Opc).addReg(DestReg).addExpr(LE),
                          getSTI());
      break;
    case LoongArch::ORI:
    case LoongArch::ADDI_W:
    case LoongArch::LD_W:
    case LoongArch::LD_D: {
      if (VK == LoongArchMCExpr::VK_LoongArch_None) {
        Out.emitInstruction(
            MCInstBuilder(Opc).addReg(DestReg).addReg(DestReg).addImm(0),
            getSTI());
        continue;
      }
      Out.emitInstruction(
          MCInstBuilder(Opc).addReg(DestReg).addReg(DestReg).addExpr(LE),
          getSTI());
      break;
    }
    case LoongArch::LU32I_D:
      Out.emitInstruction(MCInstBuilder(Opc)
                              .addReg(DestReg == TmpReg ? DestReg : TmpReg)
                              .addReg(DestReg == TmpReg ? DestReg : TmpReg)
                              .addExpr(LE),
                          getSTI());
      break;
    case LoongArch::LU52I_D:
      Out.emitInstruction(
          MCInstBuilder(Opc).addReg(TmpReg).addReg(TmpReg).addExpr(LE),
          getSTI());
      break;
    case LoongArch::ADDI_D:
      Out.emitInstruction(
          MCInstBuilder(Opc)
              .addReg(TmpReg)
              .addReg(DestReg == TmpReg ? TmpReg : LoongArch::R0)
              .addExpr(LE),
          getSTI());
      break;
    case LoongArch::ADD_D:
    case LoongArch::LDX_D:
      Out.emitInstruction(
          MCInstBuilder(Opc).addReg(DestReg).addReg(DestReg).addReg(TmpReg),
          getSTI());
      break;
    }
  }
}

void LoongArchAsmParser::emitLoadAddressAbs(MCInst &Inst, SMLoc IDLoc,
                                            MCStreamer &Out) {
  // la.abs $rd, sym
  // expands to:
  //   lu12i.w $rd, %abs_hi20(sym)
  //   ori     $rd, $rd, %abs_lo12(sym)
  //
  // for 64bit appends:
  //   lu32i.d $rd, %abs64_lo20(sym)
  //   lu52i.d $rd, $rd, %abs64_hi12(sym)
  MCRegister DestReg = Inst.getOperand(0).getReg();
  const MCExpr *Symbol = Inst.getOpcode() == LoongArch::PseudoLA_ABS
                             ? Inst.getOperand(1).getExpr()
                             : Inst.getOperand(2).getExpr();
  InstSeq Insts;

  Insts.push_back(LoongArchAsmParser::Inst(
      LoongArch::LU12I_W, LoongArchMCExpr::VK_LoongArch_ABS_HI20));
  Insts.push_back(LoongArchAsmParser::Inst(
      LoongArch::ORI, LoongArchMCExpr::VK_LoongArch_ABS_LO12));

  if (is64Bit()) {
    Insts.push_back(LoongArchAsmParser::Inst(
        LoongArch::LU32I_D, LoongArchMCExpr::VK_LoongArch_ABS64_LO20));
    Insts.push_back(LoongArchAsmParser::Inst(
        LoongArch::LU52I_D, LoongArchMCExpr::VK_LoongArch_ABS64_HI12));
  }

  emitLAInstSeq(DestReg, DestReg, Symbol, Insts, IDLoc, Out);
}

void LoongArchAsmParser::emitLoadAddressPcrel(MCInst &Inst, SMLoc IDLoc,
                                              MCStreamer &Out) {
  // la.pcrel $rd, sym
  // expands to:
  //   pcalau12i $rd, %pc_hi20(sym)
  //   addi.w/d  $rd, rd, %pc_lo12(sym)
  MCRegister DestReg = Inst.getOperand(0).getReg();
  const MCExpr *Symbol = Inst.getOperand(1).getExpr();
  InstSeq Insts;
  unsigned ADDI = is64Bit() ? LoongArch::ADDI_D : LoongArch::ADDI_W;

  Insts.push_back(LoongArchAsmParser::Inst(
      LoongArch::PCALAU12I, LoongArchMCExpr::VK_LoongArch_PCALA_HI20));
  Insts.push_back(
      LoongArchAsmParser::Inst(ADDI, LoongArchMCExpr::VK_LoongArch_PCALA_LO12));

  emitLAInstSeq(DestReg, DestReg, Symbol, Insts, IDLoc, Out);
}

void LoongArchAsmParser::emitLoadAddressPcrelLarge(MCInst &Inst, SMLoc IDLoc,
                                                   MCStreamer &Out) {
  // la.pcrel $rd, $rj, sym
  // expands to:
  //   pcalau12i $rd, %pc_hi20(sym)
  //   addi.d    $rj, $r0, %pc_lo12(sym)
  //   lu32i.d   $rj, %pc64_lo20(sym)
  //   lu52i.d   $rj, $rj, %pc64_hi12(sym)
  //   add.d     $rd, $rd, $rj
  MCRegister DestReg = Inst.getOperand(0).getReg();
  MCRegister TmpReg = Inst.getOperand(1).getReg();
  const MCExpr *Symbol = Inst.getOperand(2).getExpr();
  InstSeq Insts;

  Insts.push_back(LoongArchAsmParser::Inst(
      LoongArch::PCALAU12I, LoongArchMCExpr::VK_LoongArch_PCALA_HI20));
  Insts.push_back(LoongArchAsmParser::Inst(
      LoongArch::ADDI_D, LoongArchMCExpr::VK_LoongArch_PCALA_LO12));
  Insts.push_back(LoongArchAsmParser::Inst(
      LoongArch::LU32I_D, LoongArchMCExpr::VK_LoongArch_PCALA64_LO20));
  Insts.push_back(LoongArchAsmParser::Inst(
      LoongArch::LU52I_D, LoongArchMCExpr::VK_LoongArch_PCALA64_HI12));
  Insts.push_back(LoongArchAsmParser::Inst(LoongArch::ADD_D));

  emitLAInstSeq(DestReg, TmpReg, Symbol, Insts, IDLoc, Out);
}

void LoongArchAsmParser::emitLoadAddressGot(MCInst &Inst, SMLoc IDLoc,
                                            MCStreamer &Out) {
  // la.got $rd, sym
  // expands to:
  //   pcalau12i $rd, %got_pc_hi20(sym)
  //   ld.w/d    $rd, $rd, %got_pc_lo12(sym)
  MCRegister DestReg = Inst.getOperand(0).getReg();
  const MCExpr *Symbol = Inst.getOperand(1).getExpr();
  InstSeq Insts;
  unsigned LD = is64Bit() ? LoongArch::LD_D : LoongArch::LD_W;

  Insts.push_back(LoongArchAsmParser::Inst(
      LoongArch::PCALAU12I, LoongArchMCExpr::VK_LoongArch_GOT_PC_HI20));
  Insts.push_back(
      LoongArchAsmParser::Inst(LD, LoongArchMCExpr::VK_LoongArch_GOT_PC_LO12));

  emitLAInstSeq(DestReg, DestReg, Symbol, Insts, IDLoc, Out);
}

void LoongArchAsmParser::emitLoadAddressGotLarge(MCInst &Inst, SMLoc IDLoc,
                                                 MCStreamer &Out) {
  // la.got $rd, $rj, sym
  // expands to:
  //   pcalau12i $rd, %got_pc_hi20(sym)
  //   addi.d    $rj, $r0, %got_pc_lo12(sym)
  //   lu32i.d   $rj, %got64_pc_lo20(sym)
  //   lu52i.d   $rj, $rj, %got64_pc_hi12(sym)
  //   ldx.d     $rd, $rd, $rj
  MCRegister DestReg = Inst.getOperand(0).getReg();
  MCRegister TmpReg = Inst.getOperand(1).getReg();
  const MCExpr *Symbol = Inst.getOperand(2).getExpr();
  InstSeq Insts;

  Insts.push_back(LoongArchAsmParser::Inst(
      LoongArch::PCALAU12I, LoongArchMCExpr::VK_LoongArch_GOT_PC_HI20));
  Insts.push_back(LoongArchAsmParser::Inst(
      LoongArch::ADDI_D, LoongArchMCExpr::VK_LoongArch_GOT_PC_LO12));
  Insts.push_back(LoongArchAsmParser::Inst(
      LoongArch::LU32I_D, LoongArchMCExpr::VK_LoongArch_GOT64_PC_LO20));
  Insts.push_back(LoongArchAsmParser::Inst(
      LoongArch::LU52I_D, LoongArchMCExpr::VK_LoongArch_GOT64_PC_HI12));
  Insts.push_back(LoongArchAsmParser::Inst(LoongArch::LDX_D));

  emitLAInstSeq(DestReg, TmpReg, Symbol, Insts, IDLoc, Out);
}

void LoongArchAsmParser::emitLoadAddressTLSLE(MCInst &Inst, SMLoc IDLoc,
                                              MCStreamer &Out) {
  // la.tls.le $rd, sym
  // expands to:
  //   lu12i.w $rd, %le_hi20(sym)
  //   ori     $rd, $rd, %le_lo12(sym)
  MCRegister DestReg = Inst.getOperand(0).getReg();
  const MCExpr *Symbol = Inst.getOperand(1).getExpr();
  InstSeq Insts;

  Insts.push_back(LoongArchAsmParser::Inst(
      LoongArch::LU12I_W, LoongArchMCExpr::VK_LoongArch_TLS_LE_HI20));
  Insts.push_back(LoongArchAsmParser::Inst(
      LoongArch::ORI, LoongArchMCExpr::VK_LoongArch_TLS_LE_LO12));

  emitLAInstSeq(DestReg, DestReg, Symbol, Insts, IDLoc, Out);
}

void LoongArchAsmParser::emitLoadAddressTLSIE(MCInst &Inst, SMLoc IDLoc,
                                              MCStreamer &Out) {
  // la.tls.ie $rd, sym
  // expands to:
  //   pcalau12i $rd, %ie_pc_hi20(sym)
  //   ld.w/d    $rd, $rd, %ie_pc_lo12(sym)
  MCRegister DestReg = Inst.getOperand(0).getReg();
  const MCExpr *Symbol = Inst.getOperand(1).getExpr();
  InstSeq Insts;
  unsigned LD = is64Bit() ? LoongArch::LD_D : LoongArch::LD_W;

  Insts.push_back(LoongArchAsmParser::Inst(
      LoongArch::PCALAU12I, LoongArchMCExpr::VK_LoongArch_TLS_IE_PC_HI20));
  Insts.push_back(LoongArchAsmParser::Inst(
      LD, LoongArchMCExpr::VK_LoongArch_TLS_IE_PC_LO12));

  emitLAInstSeq(DestReg, DestReg, Symbol, Insts, IDLoc, Out);
}

void LoongArchAsmParser::emitLoadAddressTLSIELarge(MCInst &Inst, SMLoc IDLoc,
                                                   MCStreamer &Out) {
  // la.tls.ie $rd, $rj, sym
  // expands to:
  //   pcalau12i $rd, %ie_pc_hi20(sym)
  //   addi.d    $rj, $r0, %ie_pc_lo12(sym)
  //   lu32i.d   $rj, %ie64_pc_lo20(sym)
  //   lu52i.d   $rj, $rj, %ie64_pc_hi12(sym)
  //   ldx.d     $rd, $rd, $rj
  MCRegister DestReg = Inst.getOperand(0).getReg();
  MCRegister TmpReg = Inst.getOperand(1).getReg();
  const MCExpr *Symbol = Inst.getOperand(2).getExpr();
  InstSeq Insts;

  Insts.push_back(LoongArchAsmParser::Inst(
      LoongArch::PCALAU12I, LoongArchMCExpr::VK_LoongArch_TLS_IE_PC_HI20));
  Insts.push_back(LoongArchAsmParser::Inst(
      LoongArch::ADDI_D, LoongArchMCExpr::VK_LoongArch_TLS_IE_PC_LO12));
  Insts.push_back(LoongArchAsmParser::Inst(
      LoongArch::LU32I_D, LoongArchMCExpr::VK_LoongArch_TLS_IE64_PC_LO20));
  Insts.push_back(LoongArchAsmParser::Inst(
      LoongArch::LU52I_D, LoongArchMCExpr::VK_LoongArch_TLS_IE64_PC_HI12));
  Insts.push_back(LoongArchAsmParser::Inst(LoongArch::LDX_D));

  emitLAInstSeq(DestReg, TmpReg, Symbol, Insts, IDLoc, Out);
}

void LoongArchAsmParser::emitLoadAddressTLSLD(MCInst &Inst, SMLoc IDLoc,
                                              MCStreamer &Out) {
  // la.tls.ld $rd, sym
  // expands to:
  //   pcalau12i $rd, %ld_pc_hi20(sym)
  //   addi.w/d  $rd, $rd, %got_pc_lo12(sym)
  MCRegister DestReg = Inst.getOperand(0).getReg();
  const MCExpr *Symbol = Inst.getOperand(1).getExpr();
  InstSeq Insts;
  unsigned ADDI = is64Bit() ? LoongArch::ADDI_D : LoongArch::ADDI_W;

  Insts.push_back(LoongArchAsmParser::Inst(
      LoongArch::PCALAU12I, LoongArchMCExpr::VK_LoongArch_TLS_LD_PC_HI20));
  Insts.push_back(LoongArchAsmParser::Inst(
      ADDI, LoongArchMCExpr::VK_LoongArch_GOT_PC_LO12));

  emitLAInstSeq(DestReg, DestReg, Symbol, Insts, IDLoc, Out);
}

void LoongArchAsmParser::emitLoadAddressTLSLDLarge(MCInst &Inst, SMLoc IDLoc,
                                                   MCStreamer &Out) {
  // la.tls.ld $rd, $rj, sym
  // expands to:
  //   pcalau12i $rd, %ld_pc_hi20(sym)
  //   addi.d    $rj, $r0, %got_pc_lo12(sym)
  //   lu32i.d   $rj, %got64_pc_lo20(sym)
  //   lu52i.d   $rj, $rj, %got64_pc_hi12(sym)
  //   add.d     $rd, $rd, $rj
  MCRegister DestReg = Inst.getOperand(0).getReg();
  MCRegister TmpReg = Inst.getOperand(1).getReg();
  const MCExpr *Symbol = Inst.getOperand(2).getExpr();
  InstSeq Insts;

  Insts.push_back(LoongArchAsmParser::Inst(
      LoongArch::PCALAU12I, LoongArchMCExpr::VK_LoongArch_TLS_LD_PC_HI20));
  Insts.push_back(LoongArchAsmParser::Inst(
      LoongArch::ADDI_D, LoongArchMCExpr::VK_LoongArch_GOT_PC_LO12));
  Insts.push_back(LoongArchAsmParser::Inst(
      LoongArch::LU32I_D, LoongArchMCExpr::VK_LoongArch_GOT64_PC_LO20));
  Insts.push_back(LoongArchAsmParser::Inst(
      LoongArch::LU52I_D, LoongArchMCExpr::VK_LoongArch_GOT64_PC_HI12));
  Insts.push_back(LoongArchAsmParser::Inst(LoongArch::ADD_D));

  emitLAInstSeq(DestReg, TmpReg, Symbol, Insts, IDLoc, Out);
}

void LoongArchAsmParser::emitLoadAddressTLSGD(MCInst &Inst, SMLoc IDLoc,
                                              MCStreamer &Out) {
  // la.tls.gd $rd, sym
  // expands to:
  //   pcalau12i $rd, %gd_pc_hi20(sym)
  //   addi.w/d  $rd, $rd, %got_pc_lo12(sym)
  MCRegister DestReg = Inst.getOperand(0).getReg();
  const MCExpr *Symbol = Inst.getOperand(1).getExpr();
  InstSeq Insts;
  unsigned ADDI = is64Bit() ? LoongArch::ADDI_D : LoongArch::ADDI_W;

  Insts.push_back(LoongArchAsmParser::Inst(
      LoongArch::PCALAU12I, LoongArchMCExpr::VK_LoongArch_TLS_GD_PC_HI20));
  Insts.push_back(LoongArchAsmParser::Inst(
      ADDI, LoongArchMCExpr::VK_LoongArch_GOT_PC_LO12));

  emitLAInstSeq(DestReg, DestReg, Symbol, Insts, IDLoc, Out);
}

void LoongArchAsmParser::emitLoadAddressTLSGDLarge(MCInst &Inst, SMLoc IDLoc,
                                                   MCStreamer &Out) {
  // la.tls.gd $rd, $rj, sym
  // expands to:
  //   pcalau12i $rd, %gd_pc_hi20(sym)
  //   addi.d    $rj, $r0, %got_pc_lo12(sym)
  //   lu32i.d   $rj, %got64_pc_lo20(sym)
  //   lu52i.d   $rj, $rj, %got64_pc_hi12(sym)
  //   add.d     $rd, $rd, $rj
  MCRegister DestReg = Inst.getOperand(0).getReg();
  MCRegister TmpReg = Inst.getOperand(1).getReg();
  const MCExpr *Symbol = Inst.getOperand(2).getExpr();
  InstSeq Insts;

  Insts.push_back(LoongArchAsmParser::Inst(
      LoongArch::PCALAU12I, LoongArchMCExpr::VK_LoongArch_TLS_GD_PC_HI20));
  Insts.push_back(LoongArchAsmParser::Inst(
      LoongArch::ADDI_D, LoongArchMCExpr::VK_LoongArch_GOT_PC_LO12));
  Insts.push_back(LoongArchAsmParser::Inst(
      LoongArch::LU32I_D, LoongArchMCExpr::VK_LoongArch_GOT64_PC_LO20));
  Insts.push_back(LoongArchAsmParser::Inst(
      LoongArch::LU52I_D, LoongArchMCExpr::VK_LoongArch_GOT64_PC_HI12));
  Insts.push_back(LoongArchAsmParser::Inst(LoongArch::ADD_D));

  emitLAInstSeq(DestReg, TmpReg, Symbol, Insts, IDLoc, Out);
}

void LoongArchAsmParser::emitLoadImm(MCInst &Inst, SMLoc IDLoc,
                                     MCStreamer &Out) {
  MCRegister DestReg = Inst.getOperand(0).getReg();
  int64_t Imm = Inst.getOperand(1).getImm();
  MCRegister SrcReg = LoongArch::R0;

  if (Inst.getOpcode() == LoongArch::PseudoLI_W)
    Imm = SignExtend64<32>(Imm);

  for (LoongArchMatInt::Inst &Inst : LoongArchMatInt::generateInstSeq(Imm)) {
    unsigned Opc = Inst.Opc;
    if (Opc == LoongArch::LU12I_W)
      Out.emitInstruction(MCInstBuilder(Opc).addReg(DestReg).addImm(Inst.Imm),
                          getSTI());
    else
      Out.emitInstruction(
          MCInstBuilder(Opc).addReg(DestReg).addReg(SrcReg).addImm(Inst.Imm),
          getSTI());
    SrcReg = DestReg;
  }
}

bool LoongArchAsmParser::processInstruction(MCInst &Inst, SMLoc IDLoc,
                                            OperandVector &Operands,
                                            MCStreamer &Out) {
  Inst.setLoc(IDLoc);
  switch (Inst.getOpcode()) {
  default:
    break;
  case LoongArch::PseudoLA_ABS:
  case LoongArch::PseudoLA_ABS_LARGE:
    emitLoadAddressAbs(Inst, IDLoc, Out);
    return false;
  case LoongArch::PseudoLA_PCREL:
    emitLoadAddressPcrel(Inst, IDLoc, Out);
    return false;
  case LoongArch::PseudoLA_PCREL_LARGE:
    emitLoadAddressPcrelLarge(Inst, IDLoc, Out);
    return false;
  case LoongArch::PseudoLA_GOT:
    emitLoadAddressGot(Inst, IDLoc, Out);
    return false;
  case LoongArch::PseudoLA_GOT_LARGE:
    emitLoadAddressGotLarge(Inst, IDLoc, Out);
    return false;
  case LoongArch::PseudoLA_TLS_LE:
    emitLoadAddressTLSLE(Inst, IDLoc, Out);
    return false;
  case LoongArch::PseudoLA_TLS_IE:
    emitLoadAddressTLSIE(Inst, IDLoc, Out);
    return false;
  case LoongArch::PseudoLA_TLS_IE_LARGE:
    emitLoadAddressTLSIELarge(Inst, IDLoc, Out);
    return false;
  case LoongArch::PseudoLA_TLS_LD:
    emitLoadAddressTLSLD(Inst, IDLoc, Out);
    return false;
  case LoongArch::PseudoLA_TLS_LD_LARGE:
    emitLoadAddressTLSLDLarge(Inst, IDLoc, Out);
    return false;
  case LoongArch::PseudoLA_TLS_GD:
    emitLoadAddressTLSGD(Inst, IDLoc, Out);
    return false;
  case LoongArch::PseudoLA_TLS_GD_LARGE:
    emitLoadAddressTLSGDLarge(Inst, IDLoc, Out);
    return false;
  case LoongArch::PseudoLI_W:
  case LoongArch::PseudoLI_D:
    emitLoadImm(Inst, IDLoc, Out);
    return false;
  }
  Out.emitInstruction(Inst, getSTI());
  return false;
}

unsigned LoongArchAsmParser::checkTargetMatchPredicate(MCInst &Inst) {
  unsigned Opc = Inst.getOpcode();
  switch (Opc) {
  default:
    if (Opc >= LoongArch::AMADD_D && Opc <= LoongArch::AMXOR_W) {
      unsigned Rd = Inst.getOperand(0).getReg();
      unsigned Rk = Inst.getOperand(1).getReg();
      unsigned Rj = Inst.getOperand(2).getReg();
      if ((Rd == Rk || Rd == Rj) && Rd != LoongArch::R0)
        return Match_RequiresAMORdDifferRkRj;
    }
    break;
  case LoongArch::PseudoLA_PCREL_LARGE:
  case LoongArch::PseudoLA_GOT_LARGE:
  case LoongArch::PseudoLA_TLS_IE_LARGE:
  case LoongArch::PseudoLA_TLS_LD_LARGE:
  case LoongArch::PseudoLA_TLS_GD_LARGE: {
    unsigned Rd = Inst.getOperand(0).getReg();
    unsigned Rj = Inst.getOperand(1).getReg();
    if (Rd == Rj)
      return Match_RequiresLAORdDifferRj;
    break;
  }
  case LoongArch::CSRXCHG: {
    unsigned Rj = Inst.getOperand(2).getReg();
    if (Rj == LoongArch::R0 || Rj == LoongArch::R1)
      return Match_RequiresOpnd2NotR0R1;
    return Match_Success;
  }
  case LoongArch::BSTRINS_W:
  case LoongArch::BSTRINS_D:
  case LoongArch::BSTRPICK_W:
  case LoongArch::BSTRPICK_D: {
    unsigned Opc = Inst.getOpcode();
    const signed Msb =
        (Opc == LoongArch::BSTRINS_W || Opc == LoongArch::BSTRINS_D)
            ? Inst.getOperand(3).getImm()
            : Inst.getOperand(2).getImm();
    const signed Lsb =
        (Opc == LoongArch::BSTRINS_W || Opc == LoongArch::BSTRINS_D)
            ? Inst.getOperand(4).getImm()
            : Inst.getOperand(3).getImm();
    if (Msb < Lsb)
      return Match_RequiresMsbNotLessThanLsb;
    return Match_Success;
  }
  }

  return Match_Success;
}

unsigned
LoongArchAsmParser::validateTargetOperandClass(MCParsedAsmOperand &AsmOp,
                                               unsigned Kind) {
  LoongArchOperand &Op = static_cast<LoongArchOperand &>(AsmOp);
  if (!Op.isReg())
    return Match_InvalidOperand;

  MCRegister Reg = Op.getReg();
  // As the parser couldn't differentiate an FPR32 from an FPR64, coerce the
  // register from FPR32 to FPR64 if necessary.
  if (LoongArchMCRegisterClasses[LoongArch::FPR32RegClassID].contains(Reg) &&
      Kind == MCK_FPR64) {
    Op.setReg(convertFPR32ToFPR64(Reg));
    return Match_Success;
  }

  return Match_InvalidOperand;
}

bool LoongArchAsmParser::generateImmOutOfRangeError(
    OperandVector &Operands, uint64_t ErrorInfo, int64_t Lower, int64_t Upper,
    Twine Msg = "immediate must be an integer in the range") {
  SMLoc ErrorLoc = ((LoongArchOperand &)*Operands[ErrorInfo]).getStartLoc();
  return Error(ErrorLoc, Msg + " [" + Twine(Lower) + ", " + Twine(Upper) + "]");
}

bool LoongArchAsmParser::MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode,
                                                 OperandVector &Operands,
                                                 MCStreamer &Out,
                                                 uint64_t &ErrorInfo,
                                                 bool MatchingInlineAsm) {
  MCInst Inst;
  FeatureBitset MissingFeatures;

  auto Result = MatchInstructionImpl(Operands, Inst, ErrorInfo, MissingFeatures,
                                     MatchingInlineAsm);
  switch (Result) {
  default:
    break;
  case Match_Success:
    return processInstruction(Inst, IDLoc, Operands, Out);
  case Match_MissingFeature: {
    assert(MissingFeatures.any() && "Unknown missing features!");
    bool FirstFeature = true;
    std::string Msg = "instruction requires the following:";
    for (unsigned i = 0, e = MissingFeatures.size(); i != e; ++i) {
      if (MissingFeatures[i]) {
        Msg += FirstFeature ? " " : ", ";
        Msg += getSubtargetFeatureName(i);
        FirstFeature = false;
      }
    }
    return Error(IDLoc, Msg);
  }
  case Match_MnemonicFail: {
    FeatureBitset FBS = ComputeAvailableFeatures(getSTI().getFeatureBits());
    std::string Suggestion = LoongArchMnemonicSpellCheck(
        ((LoongArchOperand &)*Operands[0]).getToken(), FBS, 0);
    return Error(IDLoc, "unrecognized instruction mnemonic" + Suggestion);
  }
  case Match_InvalidOperand: {
    SMLoc ErrorLoc = IDLoc;
    if (ErrorInfo != ~0ULL) {
      if (ErrorInfo >= Operands.size())
        return Error(ErrorLoc, "too few operands for instruction");

      ErrorLoc = ((LoongArchOperand &)*Operands[ErrorInfo]).getStartLoc();
      if (ErrorLoc == SMLoc())
        ErrorLoc = IDLoc;
    }
    return Error(ErrorLoc, "invalid operand for instruction");
  }
  }

  // Handle the case when the error message is of specific type
  // other than the generic Match_InvalidOperand, and the
  // corresponding operand is missing.
  if (Result > FIRST_TARGET_MATCH_RESULT_TY) {
    SMLoc ErrorLoc = IDLoc;
    if (ErrorInfo != ~0ULL && ErrorInfo >= Operands.size())
      return Error(ErrorLoc, "too few operands for instruction");
  }

  switch (Result) {
  default:
    break;
  case Match_RequiresMsbNotLessThanLsb: {
    SMLoc ErrorStart = Operands[3]->getStartLoc();
    return Error(ErrorStart, "msb is less than lsb",
                 SMRange(ErrorStart, Operands[4]->getEndLoc()));
  }
  case Match_RequiresOpnd2NotR0R1:
    return Error(Operands[2]->getStartLoc(), "must not be $r0 or $r1");
  case Match_RequiresAMORdDifferRkRj:
    return Error(Operands[1]->getStartLoc(),
                 "$rd must be different from both $rk and $rj");
  case Match_RequiresLAORdDifferRj:
    return Error(Operands[1]->getStartLoc(), "$rd must be different from $rj");
  case Match_InvalidUImm2:
    return generateImmOutOfRangeError(Operands, ErrorInfo, /*Lower=*/0,
                                      /*Upper=*/(1 << 2) - 1);
  case Match_InvalidUImm2plus1:
    return generateImmOutOfRangeError(Operands, ErrorInfo, /*Lower=*/1,
                                      /*Upper=*/(1 << 2));
  case Match_InvalidUImm3:
    return generateImmOutOfRangeError(Operands, ErrorInfo, /*Lower=*/0,
                                      /*Upper=*/(1 << 3) - 1);
  case Match_InvalidUImm5:
    return generateImmOutOfRangeError(Operands, ErrorInfo, /*Lower=*/0,
                                      /*Upper=*/(1 << 5) - 1);
  case Match_InvalidUImm6:
    return generateImmOutOfRangeError(Operands, ErrorInfo, /*Lower=*/0,
                                      /*Upper=*/(1 << 6) - 1);
  case Match_InvalidUImm12:
    return generateImmOutOfRangeError(Operands, ErrorInfo, /*Lower=*/0,
                                      /*Upper=*/(1 << 12) - 1);
  case Match_InvalidUImm12ori:
    return generateImmOutOfRangeError(
        Operands, ErrorInfo, /*Lower=*/0,
        /*Upper=*/(1 << 12) - 1,
        "operand must be a symbol with modifier (e.g. %abs_lo12) or an "
        "integer in the range");
  case Match_InvalidUImm15:
    return generateImmOutOfRangeError(Operands, ErrorInfo, /*Lower=*/0,
                                      /*Upper=*/(1 << 15) - 1);
  case Match_InvalidSImm12:
    return generateImmOutOfRangeError(Operands, ErrorInfo, /*Lower=*/-(1 << 11),
                                      /*Upper=*/(1 << 11) - 1);
  case Match_InvalidSImm12addlike:
    return generateImmOutOfRangeError(
        Operands, ErrorInfo, /*Lower=*/-(1 << 11),
        /*Upper=*/(1 << 11) - 1,
        "operand must be a symbol with modifier (e.g. %pc_lo12) or an integer "
        "in the range");
  case Match_InvalidSImm12lu52id:
    return generateImmOutOfRangeError(
        Operands, ErrorInfo, /*Lower=*/-(1 << 11),
        /*Upper=*/(1 << 11) - 1,
        "operand must be a symbol with modifier (e.g. %pc64_hi12) or an "
        "integer in the range");
  case Match_InvalidSImm14lsl2:
    return generateImmOutOfRangeError(
        Operands, ErrorInfo, /*Lower=*/-(1 << 15), /*Upper=*/(1 << 15) - 4,
        "immediate must be a multiple of 4 in the range");
  case Match_InvalidSImm16:
    return generateImmOutOfRangeError(Operands, ErrorInfo, /*Lower=*/-(1 << 15),
                                      /*Upper=*/(1 << 15) - 1);
  case Match_InvalidSImm16lsl2:
    return generateImmOutOfRangeError(
        Operands, ErrorInfo, /*Lower=*/-(1 << 17), /*Upper=*/(1 << 17) - 4,
        "operand must be a symbol with modifier (e.g. %b16) or an integer "
        "in the range");
  case Match_InvalidSImm20:
    return generateImmOutOfRangeError(Operands, ErrorInfo, /*Lower=*/-(1 << 19),
                                      /*Upper=*/(1 << 19) - 1);
  case Match_InvalidSImm20lu12iw:
    return generateImmOutOfRangeError(
        Operands, ErrorInfo, /*Lower=*/-(1 << 19),
        /*Upper=*/(1 << 19) - 1,
        "operand must be a symbol with modifier (e.g. %abs_hi20) or an integer "
        "in the range");
  case Match_InvalidSImm20lu32id:
    return generateImmOutOfRangeError(
        Operands, ErrorInfo, /*Lower=*/-(1 << 19),
        /*Upper=*/(1 << 19) - 1,
        "operand must be a symbol with modifier (e.g. %abs64_lo20) or an "
        "integer in the range");
  case Match_InvalidSImm20pcalau12i:
    return generateImmOutOfRangeError(
        Operands, ErrorInfo, /*Lower=*/-(1 << 19),
        /*Upper=*/(1 << 19) - 1,
        "operand must be a symbol with modifier (e.g. %pc_hi20) or an integer "
        "in the range");
  case Match_InvalidSImm21lsl2:
    return generateImmOutOfRangeError(
        Operands, ErrorInfo, /*Lower=*/-(1 << 22), /*Upper=*/(1 << 22) - 4,
        "operand must be a symbol with modifier (e.g. %b21) or an integer "
        "in the range");
  case Match_InvalidSImm26Operand:
    return generateImmOutOfRangeError(
        Operands, ErrorInfo, /*Lower=*/-(1 << 27), /*Upper=*/(1 << 27) - 4,
        "operand must be a bare symbol name or an immediate must be a multiple "
        "of 4 in the range");
  case Match_InvalidImm32: {
    SMLoc ErrorLoc = ((LoongArchOperand &)*Operands[ErrorInfo]).getStartLoc();
    return Error(ErrorLoc, "operand must be a 32 bit immediate");
  }
  case Match_InvalidBareSymbol: {
    SMLoc ErrorLoc = ((LoongArchOperand &)*Operands[ErrorInfo]).getStartLoc();
    return Error(ErrorLoc, "operand must be a bare symbol name");
  }
  }
  llvm_unreachable("Unknown match type detected!");
}

extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeLoongArchAsmParser() {
  RegisterMCAsmParser<LoongArchAsmParser> X(getTheLoongArch32Target());
  RegisterMCAsmParser<LoongArchAsmParser> Y(getTheLoongArch64Target());
}
